#pragma once 

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#include <string>
#include <memory>
#include <mutex>

#include "Logger.hpp"

using namespace LogMudule;

using namespace std;

namespace SocketModule
{
    enum class SockType
    {
        ORDINARY_SOCK = 1, // 普通socket
        LISTEN_SOCK,       // 监听socket
        SERVICE_SOCK       // 服务socket
    };


    class Socket;//前置声明

    typedef shared_ptr<Socket> SockPtr;

    class Socket
    {
    public:
        static const int default_sockfd = -1;
        static const uint32_t default_ip = INADDR_ANY;
        static const uint16_t default_port = 0; // 为0的话，bind时系统会自动分配随机的端口

        static const uint32_t default_recv_size = 4096;

        bool BuildSocket(SockType type) // 指定对应socket类型，直接创建对应的socket
        {
            if (type == SockType::SERVICE_SOCK) // 无法直接创建服务socket
            {
                LOG(LogLevel::WARNING) << "无法直接创建服务socket,请通过使用监听socket调用Accept进行创建";
                return false;
            }

            SockCreation(type);
            Bind();

            if (type == SockType::LISTEN_SOCK)
            {
                Listen();
            }
            return true;
        }
        virtual ~Socket()
        {
        }
        virtual bool SockCreation(SockType type) = 0;
        virtual bool Bind() = 0;
        virtual bool Listen() = 0;
        virtual SockPtr Accept(uint32_t &ip, uint16_t &port) = 0;
        virtual void Close() = 0;
        virtual void SetBacklog(int n) = 0;
        virtual int Recv(string &in) = 0;
        virtual int Send(const string &out) = 0;

        virtual bool Recvfrom(string &in, uint32_t &ip, uint16_t &port) = 0;
        virtual bool Sendto(const string &out, const uint32_t &ip, const uint16_t &port) = 0;
        virtual bool Connect(const uint32_t &ip, const uint16_t &port) = 0;

        virtual int GetSockfd() = 0;
        virtual uint16_t GetPort() = 0;
        virtual uint32_t GetIp() = 0;
    };

    class TcpSocket : public Socket
    {
    private:
        static const int default_backlog = 8;

        virtual bool Recvfrom(string &in, uint32_t &ip, uint16_t &port)
        {
            LOG(LogLevel::WARNING) << "TcpSocket请勿调用Recvfrom,若要读取调用Recv";
            return false;
        }
        virtual bool Sendto(const string &out, const uint32_t &ip, const uint16_t &port)
        {
            LOG(LogLevel::WARNING) << "TcpSocket请勿调用Sendto,若要发送调用Send";
            return false;
        }

    public:
        TcpSocket(uint16_t port = default_port, uint32_t ip = default_ip, SockType type = SockType::ORDINARY_SOCK, int backlog = default_backlog)
            : _sockfd(default_sockfd),
              _port(port),
              _ip(ip),
              _backlog(backlog),
              _iscorrection(false),
              _sock_type(type)
        {
        }
        virtual bool SockCreation(SockType type)
        {
            if (type == SockType::SERVICE_SOCK) // 无法直接创建服务socket
            {
                LOG(LogLevel::WARNING) << "无法直接创建服务socket,请通过使用监听socket调用Accept进行创建";
                return false;
            }

            lock_guard<mutex> lock(_mutex);

            _sock_type = type; // 设置socket类型

            // 不为默认值就表示已经socket过了或者是  通过Accept得到的
            if (_sockfd != default_sockfd)
            {
                LOG(LogLevel::WARNING) << "同一个socket请勿重复调用SockCreation";
                return false;
            }

            int n = socket(AF_INET, SOCK_STREAM, 0);
            if (n > 0)
            {
                _sockfd = n;
                LOG(LogLevel::ERROR) << "socket成功";
                return true;
            }
            else
            {
                LOG(LogLevel::ERROR) << "socket error";
            }
            return false;
        }
        virtual bool Bind()
        {
            lock_guard<mutex> lock(_mutex);

            if (_sock_type == SockType::SERVICE_SOCK)
            {
                LOG(LogLevel::WARNING) << "服务socket无需再bind";
                return false;
            }

            struct sockaddr_in addr;
            addr.sin_family = AF_INET;
            addr.sin_addr.s_addr = htonl(_ip);
            addr.sin_port = htons(_port);

            int n = bind(_sockfd, (sockaddr *)&addr, sizeof(addr));
            if (n == 0)
            {
                struct sockaddr_in getaddr;
                socklen_t len = sizeof(getaddr);
                getsockname(_sockfd, (struct sockaddr *)&getaddr, &len);

                // 如果端口是操作系统随机分配的，就获取一下，进行修正
                _port = ntohs(getaddr.sin_port);
                _ip = ntohl(getaddr.sin_addr.s_addr);

                LOG(LogLevel::ERROR) << "bind成功";
                return true;
            }
            else
            {
                LOG(LogLevel::ERROR) << "bind error";
            }
            return false;
        }
        virtual bool Listen() // 把当前TCPsocket设置为监听状态
        {
            lock_guard<mutex> lock(_mutex);
            if (_sock_type == SockType::LISTEN_SOCK) // 当前TCPsocket是listensocket，才能设置监听状态
            {
                int n = listen(_sockfd, _backlog);
                if (n == 0)
                {
                    LOG(LogLevel::ERROR) << "listen成功";
                    return true;
                }
                else
                {
                    LOG(LogLevel::ERROR) << "listen error";
                }
            }
            return false;
        }
        virtual SockPtr Accept(uint32_t &ip, uint16_t &port) // 只能由listensocket调用
        {
            lock_guard<mutex> lock(_mutex);

            if (_sock_type != SockType::LISTEN_SOCK) // 当前TCPsocket不是listensocket
            {
                LOG(LogLevel::ERROR) << "只能由listensocket调用Accept";
                return nullptr;
            }
            struct sockaddr_in addr;
            socklen_t len = sizeof(addr);
         
            int n = accept(_sockfd, (struct sockaddr*)&addr, &len);

            if (n > 0) // 获取新连接成功
            {
                ip = ntohl(addr.sin_addr.s_addr);
                port = ntohs(addr.sin_port);

                shared_ptr<TcpSocket> tmp = make_shared<TcpSocket>(_port, _ip, SockType::SERVICE_SOCK);
                tmp->SetSockdfd(n); // 修正sockfd

                LOG(LogLevel::ERROR) << "accept成功";
                return tmp;
            }
            else
            {
                LOG(LogLevel::ERROR) << "accept error";
            }
            return nullptr;
        }
        // 返回值含义和recv相同
        virtual int Recv(string &in) // 只能由  非  listensocket调用
        {
            lock_guard<mutex> lock(_mutex);
            if (_sock_type == SockType::LISTEN_SOCK) // 只能由  非  listensocket调用
            {
                LOG(LogLevel::ERROR) << "只能由非listensocket调用Recv";
                return -1;
            }
            char rbuf[default_recv_size];
            
            int n = recv(_sockfd, rbuf, default_recv_size, 0);
            if (n > 0) // 读取成功
            {
                rbuf[n] = '\0';
                in = rbuf;
            }
            return n;
        }
        // 返回值含义和send相同
        virtual int Send(const string &out)
        {
            lock_guard<mutex> lock(_mutex);
            if (_sock_type == SockType::LISTEN_SOCK) // 只能由  非  listensocket调用
            {
                LOG(LogLevel::ERROR) << "只能由非listensocket调用Send";
                return -1;
            }
            int n = send(_sockfd, out.c_str(), out.size(), 0);
            return n;
        }

        virtual void Close()
        {
            lock_guard<mutex> lock(_mutex);
            close(_sockfd);
            _sockfd = default_sockfd;
        }
        virtual void SetBacklog(int n)
        {
            lock_guard<mutex> lock(_mutex);
            _backlog = n;
        }

        void SetSockdfd(int sockfd) // Tcpsocket自己的成员函数，因为不想让用户随意修改sockfd
        {
            lock_guard<mutex> lock(_mutex);
            _sockfd = sockfd;
        }

        virtual bool Connect(const uint32_t &ip, const uint16_t &port)
        {
            lock_guard<mutex> lock(_mutex);
            if (_sock_type != SockType::ORDINARY_SOCK) // 只能由普通socket调用
            {
                LOG(LogLevel::ERROR) << "只能由普通socket调用Connect";
                return false;
            }

            struct sockaddr_in addr;
            addr.sin_family=AF_INET;
            addr.sin_addr.s_addr=htonl(ip);
            addr.sin_port=htons(port);

            int n = connect(_sockfd, (const struct sockaddr *)&addr, sizeof(addr));
            if (n == 0) // 连接成功
            {
                return true;
            }
            else
            {
                LOG(LogLevel::ERROR) << "connect error";
            }
            return false;
        }
        virtual uint16_t GetPort()
        {
            lock_guard<mutex> lock(_mutex); // 加锁

            if (_iscorrection == false)
            {
                _iscorrection = true;
                // 用户需要时，以防万一修正一下
                struct sockaddr_in getaddr;
                socklen_t len = sizeof(getaddr);
                getsockname(_sockfd, (struct sockaddr *)&getaddr, &len);

                // 如果端口是操作系统随机分配的，就获取一下，进行修正
                _port = ntohs(getaddr.sin_port);
                _ip = ntohl(getaddr.sin_addr.s_addr);
            }
            return _port;
        }
        virtual uint32_t GetIp()
        {
            lock_guard<mutex> lock(_mutex);

            // 如果修正过了，就不要进行重复修正了
            // 因为getsockname是系统调用，调用开销大
            if (_iscorrection == false)
            {
                // 用户需要时，以防万一修正一下
                _iscorrection = true;
                struct sockaddr_in getaddr;
                socklen_t len = sizeof(getaddr);
                getsockname(_sockfd, (struct sockaddr *)&getaddr, &len);

                // 如果端口是操作系统随机分配的，就获取一下，进行修正
                _port = ntohs(getaddr.sin_port);
                _ip = ntohl(getaddr.sin_addr.s_addr);
            }

            return _ip;
        }

        virtual int GetSockfd()
        {
            lock_guard<mutex> lock(_mutex);
            return _sockfd;
        }

        virtual ~TcpSocket()
        {
            _mutex.lock();
            if (_sockfd != default_sockfd)
            {
                _mutex.unlock();

                Close();
            }
        }

    private:
        SockType _sock_type; // socket的类型
        bool _iscorrection;  // 标识ip和端口是否已经修正过了
        int _sockfd;
        uint16_t _port;
        uint32_t _ip;
        int _backlog; // 全连接个数

        mutex _mutex;
    };

    class UdpSocket : public Socket
    {
    private:
        virtual bool Listen()
        {
            LOG(LogLevel::WARNING) << "UdpSocket请勿调用Listen";
            return false;
        }
        virtual SockPtr Accept(uint32_t &ip, uint16_t &port)
        {
            LOG(LogLevel::WARNING) << "UdpSocket请勿调用Accept";
            return nullptr;
        }
        virtual void SetBacklog(int n)
        {
            LOG(LogLevel::WARNING) << "UdpSocket请勿调用SetBacklog";
        }
        virtual int Recv(string &in)
        {
            LOG(LogLevel::WARNING) << "UdpSocket请勿调用Recv,若要读取调用Recvfrom";
            return -1;
        }
        virtual int Send(const string &out)
        {
            LOG(LogLevel::WARNING) << "UdpSocket请勿调用Recv,若要发送请调用Sendto";
            return -1;
        }
        virtual bool Connect(const uint32_t &ip, const uint16_t &port)
        {
            LOG(LogLevel::WARNING) << "UdpSocket请勿调用Connect";
            return false;
        }

    public:
        UdpSocket(uint16_t port = default_port, uint32_t ip = default_ip, SockType type = SockType::ORDINARY_SOCK)
            : _sockfd(default_sockfd),
              _port(port),
              _ip(ip),
              _iscorrection(false),
              _sock_type(type) // socket的类型
        {
        }
        virtual bool SockCreation(SockType type)
        {
            if (type != SockType::ORDINARY_SOCK)
            {
                LOG(LogLevel::ERROR) << "UdpSocket的类型只能是ORDINARY_SOCK";
                return false;
            }

            lock_guard<mutex> lock(_mutex); // 保护_sockfd
            _sock_type = type;

            int n = socket(AF_INET, SOCK_DGRAM, 0);
            if (n > 0)
            {
                _sockfd = n;
                return true;
            }
            else
            {
                LOG(LogLevel::ERROR) << "socket error";
            }
            return false;
        }
        virtual bool Bind()
        {
            lock_guard<mutex> lock(_mutex); // 保护_sockfd

            struct sockaddr_in addr;
            addr.sin_family = AF_INET;
            addr.sin_addr.s_addr = htonl(_ip);
            addr.sin_port = htons(_port);

            int n = bind(_sockfd, (sockaddr *)&addr, sizeof(addr));
            if (n == 0)
            {
                return true;
            }
            else
            {
                LOG(LogLevel::ERROR) << "bind error";
            }
            return false;
        }

        virtual void Close()
        {
            lock_guard<mutex> lock(_mutex); // 保护_sockfd
            close(_sockfd);
            _sockfd = default_sockfd;
        }

        virtual bool Recvfrom(string &in, uint32_t &ip, uint16_t &port)
        {
            lock_guard<mutex> lock(_mutex); // 保护_sockfd

            char rbuf[default_recv_size];

            struct sockaddr_in addr;
            socklen_t len = sizeof(addr);
            int n = recvfrom(_sockfd, rbuf, default_recv_size, 0, (struct sockaddr *)&addr, &len);
            if (n > 0) // 读取成功
            {
                ip=ntohl(addr.sin_addr.s_addr);
                port=ntohs(addr.sin_port);

                rbuf[n] = '\0';
                in = rbuf;
                return true;
            }
            else
            {
                LOG(LogLevel::ERROR) << "recvfrom error";
            }
            return false;
        }
        virtual bool Sendto(const string &out, const uint32_t &ip, const uint16_t &port)
        {
            lock_guard<mutex> lock(_mutex); // 保护_sockfd

            struct sockaddr_in addr;
            addr.sin_family=AF_INET;
            addr.sin_addr.s_addr=htonl(ip);
            addr.sin_port=htons(port);

            int n = sendto(_sockfd, out.c_str(), out.size(), 0, (const struct sockaddr *)&addr, sizeof(addr));
            if (n > 0) // 发送成功
            {
                return true;
            }
            else
            {
                LOG(LogLevel::ERROR) << "sendto error";
            }
            return false;
        }
        virtual int GetSockfd()
        {
            lock_guard<mutex> lock(_mutex); // 加锁
            return _sockfd;
        }
        virtual uint16_t GetPort()
        {
            lock_guard<mutex> lock(_mutex); // 加锁

            if (_iscorrection == false)
            {
                _iscorrection = true;
                // 用户需要时，以防万一修正一下
                struct sockaddr_in getaddr;
                socklen_t len = sizeof(getaddr);
                getsockname(_sockfd, (struct sockaddr *)&getaddr, &len);

                // 如果端口是操作系统随机分配的，就获取一下，进行修正
                _port = ntohs(getaddr.sin_port);
                _ip = ntohl(getaddr.sin_addr.s_addr);
            }
            return _port;
        }
        virtual uint32_t GetIp()
        {
            lock_guard<mutex> lock(_mutex);

            // 如果修正过了，就不要进行重复修正了
            // 因为getsockname是系统调用，调用开销大
            if (_iscorrection == false)
            {
                // 用户需要时，以防万一修正一下
                _iscorrection = true;
                struct sockaddr_in getaddr;
                socklen_t len = sizeof(getaddr);
                getsockname(_sockfd, (struct sockaddr *)&getaddr, &len);

                // 如果端口是操作系统随机分配的，就获取一下，进行修正
                _port = ntohs(getaddr.sin_port);
                _ip = ntohl(getaddr.sin_addr.s_addr);
            }

            return _ip;
        }

        virtual ~UdpSocket()
        {
            _mutex.lock();
            if (_sockfd != default_sockfd)
            {
                _mutex.unlock();
                Close();
            }
        }

    private:
        SockType _sock_type; // socket的类型
        int _sockfd;
        uint16_t _port;
        uint32_t _ip;
        bool _iscorrection;
        mutex _mutex;
    };
}
